<template>
  <div class="chart-wrapper">
    <div class="errorTip" v-if="errorTip">
      <img src="@/assets/exclamation.png" alt="" />
      <span>{{ errorText }}</span>
      <el-button
        class="eig-btn"
        style="width: 45%"
        type="primary"
        @click="back()"
        >重试</el-button
      >
    </div>
    <div class="errorTip" v-if="connectErrorTip && !errorTip">
      <img src="@/assets/exclamation.png" alt="" />
      <span>连接异常断开</span>
      <el-button
        class="eig-btn"
        style="width: 45%"
        type="primary"
        @click="back()"
        >回退</el-button
      >
    </div>
    <div class="errorTip" v-if="successTip">
      <img src="@/assets/exclamation.png" alt="" />
      <span>重新连接</span>
      <el-button
        class="eig-btn"
        style="width: 45%"
        type="primary"
        @click="init()"
        >确认</el-button
      >
    </div>
    <div class="chart-prospect" v-if="prospect">
      <img src="@/assets/loading.gif" alt="" />
    </div>
    <div id="media">
      <audio class="audio-box" ref="audioPlayer" controls></audio>
      <audio id="audio" autoplay="true"></audio>
      <div id="webgl" class="preview-canvas"></div>
    </div>

    <div class="option" style="display: none">
      <input id="use-stun" type="checkbox" checked />
      <label for="use-stun">对话数字人</label>
    </div>

    <div class="chart-header"></div>
    <!-- <div class="start-btn" @click="toggleOpen">模拟</div> -->
    <div
      class="chart-scrollbar-wrapper"
      ref="msgWrapper"
      :class="{ extra: isExtra }"
    >
      <el-scrollbar
        :style="{ opacity: chartList.length ? 1 : 0 }"
        class="chart-scrollbar"
        ref="scrollbar"
      >
        <ul class="msg-list" ref="msgList">
          <!-- <li class="introduce-item">
          <div class="introduce">
            <div>你可以这样问我：</div>
            <div>广研什么时候成立的呢</div>
            <div>广研什么时候成立的呢</div>
            <div>广研什么时候成立的呢</div>
          </div>
        </li> -->
          <li class="msg-item" v-for="(item, index) in chartList" :key="index">
            <div class="msg" :class="item.type" @click="playRecording(item)">
              <span v-if="!item.icon">{{ item.text }}</span>
              <img
                v-if="item.icon && item.type === 'send'"
                class="point"
                src="@/assets/whitePoint.gif"
                alt=""
              />
              <img
                v-if="item.icon && item.type === 'receive'"
                class="point"
                src="@/assets/greenPoint.gif"
                alt=""
              />
            </div>
          </li>
        </ul>
      </el-scrollbar>
    </div>
    <el-dialog
      title="提示"
      :close-on-click-modal="true"
      :visible.sync="dialogVisible"
      class="talk-dialog"
    >
      <div class="dialog-header">
        <div class="dialog-img">
          <img src="@/assets/dialogue.png" alt="" />
          <span>请选择“是”或“否”</span>
        </div>
        <div>
          <span>下一题</span>
          <span>{{ time }}s</span>
        </div>
      </div>
      <div class="dialog-content">
        <!-- <div class="dialog-tip" v-if="msgIndex < msgList.length - 1">
          {{ msgList[msgIndex].key }}
        </div> -->
        <el-radio-group @input="changRadio" class="whether" v-model="radio">
          <el-radio :label="1">是</el-radio>
          <el-radio :label="0">否</el-radio>
        </el-radio-group>
      </div>
    </el-dialog>
    <!-- <div class="control-wrapper">
      <audio class="audio-box" ref="audioPlayer" controls></audio>
      <div v-if="isMouseDown" class="btn-tip">松手发送，上移取消</div>
      <div
        class="voice-wrapper"
        :class="{ active: isMouseDown }"
        @mousedown="mouseDownControl"
        @mouseup="mouseUpControl"
        @mouseleave="mouseOutControl"
      >
        <div class="touch">
          <b v-if="!isMouseDown">按住说话</b>
          <av-media
            v-else
            class="av-media"
            :media="mediaStream"
            type="frequ"
            frequ-direction="mo"
            :canv-height="60"
            :frequ-lnum="60"
            line-color="#fff"
          />
        </div>
        <img src="" alt="" />
      </div>
    </div> -->
  </div>
</template>

<script>
import { mapState } from "vuex";
import * as THREE from "@/utils/three.module.js";
import human from "@/assets/video/human.mp4";
import { start, stop, pcConnected } from "@/utils/client.js";
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
export default {
  data() {
    return {
      recorder: null,
      scriptProcessor: null,
      mediaStream: null,
      isMouseDown: false,
      isCancel: true,
      chartList: [],
      audioChunks: [],
      delay: 100,
      type: "webm", //webm,pcm
      preVideo: null,
      isOpen: false,
      recordTime: 5000,
      msgList: [
        {
          key: "是否应该关注基金的长期业绩表现来评估其表现？",
        },
        {
          key: "是否应该了解基金经理的背景和经验来评估基金的管理能力？",
        },
        {
          key: "是否需要考虑基金的费用来控制投资成本？",
        },
        {
          key: "是否应该关注基金的历史风险水平来评估其风险承受能力？",
        },
        {
          key: "是否应该选择成立时间较长的基金来更全面地评估其表现？‌ ",
        },
      ],
      msgIndex: 0,
      ws: null,
      dialogVisible: false,
      radio: false,
      time: 10,
      prospect: true,
      errorTip: false,
      isExtra: false,
      successTip: true,
      connectErrorTip: false,
      errorText: "数字人加载失败",
    };
  },
  computed: {},
  watch: {},
  created() {
    let isHome = localStorage.getItem("home") == "true" ? true : false;
    if (isHome) {
      this.successTip = false;
    }
    localStorage.setItem("home", false);
    this.errorTip = false;
    this.w_h = 1080 / 1920;
  },
  mounted() {
    if (!this.successTip) {
      this.init();
    }
  },
  beforeDestroy() {
    stop();
    if (this.mediaStream) {
      this.mediaStream.getTracks().forEach((track) => {
        track.stop();
      });
      this.mediaStream = null;
    }
    this.ws && this.ws.close();
  },
  methods: {
    init() {
      this.successTip = false;
      this.onlineTimer && clearTimeout(this.onlineTimer);
      this.onlineTimer = setTimeout(() => {
        this.errorText = "数字人加载超时";
        this.errorTip = true;
      }, 20000);
      this.$axios.get("/api2/onlineCount").then((res) => {
        if (res.data.data.onlineCount) {
          this.$message.error("人数已满，请稍后再试！");
          this.$router.push("/");
        } else {
          this.initWs()
            .then(() => {
              this.initVideo();
              this.renderHuman();
              start(this.preVideo)
                .then(() => {
                  this.onlineTimer && clearTimeout(this.onlineTimer);
                  this.prospect = false;
                  setTimeout(() => {
                    this.sendAnswerToBack(this.msgList[this.msgIndex].key);
                  }, 3000);
                })
                .catch(() => {
                  this.errorText = "数字人加载失败";
                  this.errorTip = true;
                });
            })
            .catch(() => {
              this.errorText = "Websocket证书无效";
              this.errorTip = true;
            });
        }
      });
    },
    initWs() {
      return new Promise((resolve, reject) => {
        this.ws = new WebSocket("wss://106.53.187.94:8001");
        this.ws.onopen = () => {
          console.log("连接打开");
          // this.ws.send("Hello, server!");
          resolve();
          setTimeout(() => {
            this.ws.send("1");
          }, 100);
        };

        this.ws.onmessage = (e) => {
          console.log("收到消息：" + e.data);
          let data = JSON.parse(e.data);

          if (data.code === 200) return;
          if (data.type === "ping") return;
          if (data.status == 1) {
            this.receiveData(this.msgList[this.msgIndex].key);
          }
          if (data.status == 2) {
            setTimeout(() => {
              this.radio = -1;
              this.dialogVisible = true;
              this.countTimer = setInterval(() => {
                this.time--;
                if (this.time == 0) {
                  this.radio = 1;
                  this.changRadio(1);
                  clearInterval(this.countTimer);
                  setTimeout(() => {
                    this.dialogVisible = false;
                  }, 1000);
                }
              }, 1000);
            }, 3000);
            // this.autoClick();
          }
        };

        this.ws.onerror = (event) => {
          console.log("event", event);
          console.log("错误发生");
          reject("websocket连接错误");
        };

        this.ws.onclose = () => {
          this.connectErrorTip = true;
          stop();
          if (this.mediaStream) {
            this.mediaStream.getTracks().forEach((track) => {
              track.stop();
            });
            this.mediaStream = null;
          }
          console.log("连接关闭");
        };
      });
    },
    goBack(text = "网络超时，请稍后再试！") {
      this.$message.error(text);
      setTimeout(() => {
        this.$router.push("/");
      }, 3000);
    },
    back() {
      this.$router.go(-1);
    },
    retry() {
      this.$router.go(0);
    },
    changRadio(value) {
      this.countTimer && clearInterval(this.countTimer);
      this.dialogVisible = false;
      this.chartList.push({
        type: "send",
        icon: true,
        text: "",
      });
      this.sendData(value ? "是" : "否");
      setTimeout(() => {
        this.msgIndex++;
        if (this.msgIndex < this.msgList.length) {
          this.time = 10;
          this.sendAnswerToBack(this.msgList[this.msgIndex].key);
        }
      }, 2000);
    },
    // 隐藏按钮开始流程
    toggleOpen() {
      if (this.isOpen) {
        this.isOpen = false;
        this.preVideo.pause();
        stop();
      } else {
        this.autoClick();
        this.isOpen = true;
        this.preVideo.currentTime = 0;
        this.preVideo.play();
        start(this.preVideo);
      }
    },
    // 自动点击录音
    autoClick() {
      setTimeout(() => {
        this.$message.error("请说出您的回答");
        this.mouseDownControl();
        setTimeout(() => {
          this.mouseUpControl();
        }, this.recordTime);
      }, 4000);
    },
    // 初始化视频
    initVideo() {
      this.preVideo = document.createElement("video");
      this.preVideo.muted = true;
      this.preVideo.play();
    },
    // 渲染数字人
    renderHuman() {
      window.onresize = function () {
        const canvasHeight = window.innerHeight * 0.94;
        const canvasWidth = canvasHeight * this.w_h;
        renderer.setSize(canvasWidth, canvasHeight);
        camera.aspect = canvasWidth / canvasHeight;
        camera.updateProjectionMatrix();
      };

      // const canvasWidth = 352;
      // const canvasHeight = 1280;
      const canvasHeight = window.innerHeight * 0.94;
      const canvasWidth = canvasHeight * this.w_h;

      const camera = new THREE.OrthographicCamera(
        canvasWidth / -2,
        canvasWidth / 2,
        canvasHeight / 2,
        canvasHeight / -2,
        1,
        1000
      );
      camera.position.z = 1000;

      const renderer = new THREE.WebGLRenderer({
        antialias: false,
        alpha: true,
      });
      renderer.outputColorSpace = THREE.SRGBColorSpace;
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(canvasWidth, canvasHeight);
      renderer.setClearColor(0x000000, 0);
      document.getElementById("webgl").appendChild(renderer.domElement);

      const scene = new THREE.Scene();
      const textureLoader = new THREE.TextureLoader();
      const texture = new THREE.VideoTexture(this.preVideo);

      texture.flipY = true;
      texture.colorSpace = THREE.SRGBColorSpace;
      const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight);
      const vertexShader = `
  varying vec2 vUv;
	void main() {
		vUv = uv;
		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
	}`;
      const fragmentShader = `
  uniform sampler2D videoTexture;
	uniform float threshold;
	varying vec2 vUv;
	void main() {
		vec4 color = texture2D(videoTexture, vUv);
		float greenScreen = color.g - max(color.r, color.b);
		float alpha = 1.0 - smoothstep(threshold - 0.05, threshold + 0.05, greenScreen);
		gl_FragColor = vec4(color.rgb, alpha);
	}`;
      var material = new THREE.ShaderMaterial({
        uniforms: {
          videoTexture: {
            value: texture,
          },
          threshold: {
            value: 0.05,
          },
        },
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
        side: THREE.DoubleSide,
        transparent: true,
      });
      const mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);

      function animate() {
        requestAnimationFrame(animate);
        if (pcConnected) {
          renderer.render(scene, camera);
        }
      }

      animate();
    },

    // 发送文本数据
    sendData(text) {
      let reversedArr = [...this.chartList].reverse();
      let index = reversedArr.findIndex((item) => {
        return item.type === "send";
      });
      if (index == -1) return;
      this.showText(text, this.chartList, this.chartList.length - index - 1);
      this.$nextTick(() => {
        this.judgeExtra();
        this.scrollToBottom();
      });
    },
    // 接受文本数据
    receiveData(text) {
      this.chartList.push({
        type: "receive",
        icon: true,
        text: "",
      });
      let reversedArr = [...this.chartList].reverse();
      let index = reversedArr.findIndex((item) => {
        return item.type === "receive";
      });
      if (index == -1) return;
      this.showText(text, this.chartList, this.chartList.length - index - 1);
      this.$nextTick(() => {
        this.scrollToBottom();
      });
    },
    judgeExtra() {
      if (
        this.$refs.msgList.clientHeight >
        this.$refs.msgWrapper.clientHeight - 20
      ) {
        this.isExtra = true;
      }
    },
    // 把问题发到后端
    sendAnswerToBack(text) {
      this.humanTimer && clearTimeout(this.humanTimer);
      this.humanTimer = setTimeout(() => {
        this.$message.error("网络超时，请稍后再试！");
      }, 10000);
      this.$axios
        .post("/api2/human", {
          interrupt: true,
          type: "echo",
          text: text,
        })
        .then((res) => {
          console.log("human", res);
          // 假设websoket返回触发
          // setTimeout(() => {
          //   this.receiveData(this.msgList[this.msgIndex].key);

          //   this.autoClick();
          // }, 3000);
        })
        .catch((error) => {
          this.$message.error(error.message);
        })
        .finally(() => {
          this.humanTimer && clearTimeout(this.humanTimer);
        });
    },
    // 音频转文本
    audioToText(audioChunks) {
      // 将音频转成wav并且上传到后端解析成文本
      let blob = new Blob(audioChunks, {
        type: "audio/wav;codecs=opus",
      });
      const file = new File([blob], "audio.wav", { type: "audio/wav" });
      const formdata = new FormData();
      formdata.append("file", file);
      this.uploadTimer && clearTimeout(this.uploadTimer);
      this.uploadTimer = setTimeout(() => {
        this.$message.error("网络超时，请稍后再试！");
      }, 10000);
      this.$axios
        .post("/api1/uploadnproc", formdata, {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        })
        .then((res) => {
          let text = res.data.data[1].res;
          this.sendData(text);
          setTimeout(() => {
            this.msgIndex++;
            if (this.msgIndex < this.msgList.length) {
              this.sendAnswerToBack(this.msgList[this.msgIndex].key);
            }
          }, 2000);
        })
        .catch((error) => {
          this.chartList.pop();
          this.$message.error(error.message);
        })
        .finally(() => {
          this.uploadTimer && clearTimeout(this.uploadTimer);
        });
    },
    // 鼠标按下
    mouseDownControl() {
      this.isMouseDown = true;
      this.audioChunks = [];
      navigator.mediaDevices
        .getUserMedia({ audio: true })
        .then((stream) => {
          this.mediaStream = stream;
          if (this.type == "webm") {
            this.isCancel = false;
            this.recorder = new MediaRecorder(stream, { type: "audio/wav" });
            this.recorder.start();
            this.recorder.ondataavailable = (e) => {
              if (this.isCancel) return;
              this.audioChunks.push(e.data);
              // 先发送个空的
              this.chartList.push({
                type: "send",
                icon: true,
                text: "",
                audioChunks: this.audioChunks,
              });
              this.audioToText(this.audioChunks);
            };
          } else if (this.type == "pcm") {
            const source = audioContext.createMediaStreamSource(stream);
            this.scriptProcessor = audioContext.createScriptProcessor(
              4096,
              1,
              1
            );
            source.connect(this.scriptProcessor);
            this.scriptProcessor.connect(audioContext.destination);
            this.scriptProcessor.onaudioprocess = (e) => {
              var inputData = e.inputBuffer.getChannelData(0);
              this.audioChunks.push(...inputData);
              // this.audioChunks.push(inputData.buffer);
            };
          }
        })
        .catch((err) => {
          console.log(err.message);
        });
    },
    // 鼠标抬起
    mouseUpControl() {
      if (this.mediaStream) {
        this.mediaStream.getTracks().forEach((track) => {
          track.stop();
        });
        this.mediaStream = null;
      }
      if (this.type == "webm") {
        if (this.recorder && this.recorder.state == "recording") {
          this.recorder.stop();
        }
      } else if (this.type == "pcm") {
        // this.sendData(this.audioChunks);
        this.scriptProcessor && this.scriptProcessor.disconnect();
        this.scriptProcessor = null;
      }

      this.isMouseDown = false;
    },
    // 鼠标移出
    mouseOutControl() {
      if (this.mediaStream) {
        this.mediaStream.getTracks().forEach((track) => {
          track.stop();
        });
        this.mediaStream = null;
      }
      if (this.type == "webm") {
        if (this.recorder && this.recorder.state == "recording") {
          this.recorder.stop();
          this.isCancel = true;
          this.audioChunks = [];
        }
      } else if (this.type == "pcm") {
        this.scriptProcessor && this.scriptProcessor.disconnect();
        this.scriptProcessor = null;
      }
      this.isMouseDown = false;
    },
    // 滚动底部
    scrollToBottom() {
      if (this.$refs.scrollbar) {
        const container = this.$refs.scrollbar.$el.querySelector(
          ".el-scrollbar__wrap"
        );
        container.style.scrollBehavior = "smooth";
        container.scrollTop = container.scrollHeight;
      }
    },
    // 显示文本
    showText(text = "", list, i) {
      if (text.length == 0) {
        this.$set(list[i], "text", text);
      }
      this.$set(list[i], "icon", false);
      let index = 0;
      list[i].timer = setInterval(() => {
        if (index < text.length) {
          index++;
          this.$set(list[i], "text", text.substring(0, index));
          this.$nextTick(() => {
            this.judgeExtra();
            this.scrollToBottom();
          });
        } else {
          list[i].timer && clearInterval(list[i].timer);
        }
      }, this.delay);
    },
    // 播放录音
    playRecording(item) {
      if (item.audioChunks) {
        if (this.type == "webm") {
          const superBuffer = new Blob(item.audioChunks, {
            type: "audio/webm",
          });
          this.$refs.audioPlayer.src = URL.createObjectURL(superBuffer);
          this.$refs.audioPlayer.play();
          this.downLoadWav(item.audioChunks);
        } else if (this.type == "pcm") {
          this.palyPCM(item.audioChunks);
        }
      }
    },
    // pcm播放
    palyPCM(pcmData) {
      const sourceNode = audioContext.createBufferSource();
      sourceNode.connect(audioContext.destination);
      // let floatArray = new Float32Array(pcmData);
      let audioBuffer = audioContext.createBuffer(1, pcmData.length, 48000);

      audioBuffer.getChannelData(0).set(pcmData);
      // let audioBufferChannel =
      // for (let i = 0; i < 4096; i++) {
      //   audioBufferChannel[i] = floatArray[i];
      // }
      for (let i = 0; i < array.length; i++) {
        const element = array[i];
      }

      sourceNode.buffer = audioBuffer;

      sourceNode.start();
    },
    // 下载Wav
    downLoadWav(audioChunks) {
      const audioBlob = new Blob(audioChunks, { type: "audio/wav" });
      const url = URL.createObjectURL(audioBlob);
      const a = document.createElement("a");
      a.href = url;
      a.download = "eig.wav";
      a.click();
    },
  },
};
</script>
<style lang="scss" scoped></style>
